오픈소스 기여 방법 및 순서

회사([[[Git] 브랜치 관리 전략 (Branch Management Gu - 핵심 개념 및 특징 정리|브랜치 관리 전략]])는 GitLab 기반으로 하나의 저장소를 여러 사람이 직접 브랜치를 파서 공유하는 방식이지만, 오픈소스는 대부분 자신의 계정에 저장소를 fork(복제) 한 뒤 거기서 작업하고, upstream 원본 저장소로 PR을 보내는 방식이다. 이 차이 때문에 순서와 용어가 회사 방식과 꽤 다르다. GlueSQL에 실제로 적용한 사례는 [[[프로젝트] GlueSQL PR-이슈 작성법|GlueSQL PR-이슈 작성법]] 참고.

1. 프로젝트 파악 — 규칙부터 확인

기여를 시작하기 전에 그 프로젝트만의 관례를 먼저 읽는다. 관례를 안 지키면 코드가 맞아도 리젝될 수 있다.

  • README.md: 빌드 방법, 개발 환경 요구사항
  • CONTRIBUTING.md (있으면): 브랜치 네이밍, 커밋 메시지 규칙, PR 템플릿, 코드 스타일
  • .github/ 폴더: PR/이슈 템플릿(.github/PULL_REQUEST_TEMPLATE.md, .github/ISSUE_TEMPLATE/), CI 워크플로(.github/workflows/) — CI가 뭘 검사하는지 보면 PR 제출 전에 로컬에서 뭘 돌려봐야 하는지 알 수 있다.
  • CONTRIBUTING 문서가 없는 프로젝트도 많다. 이 경우 최근 머지된 PR/이슈 10~20개를 직접 열어서 실제 관례를 역으로 추론해야 한다 — GlueSQL이 이 경우였다(공식 CONTRIBUTING.md 없음, 커뮤니티 멘티 가이드만 존재).
  • 코드 컨벤션: linter/formatter 설정 파일(.eslintrc, rustfmt.toml, .clippy.toml 등)이 있으면 그게 곧 스타일 가이드. 없으면 기존 코드 스타일을 그대로 따라간다.
  • 테스트 실행법: README나 CI 워크플로 파일에서 테스트 명령어를 찾는다(예: Rust면 cargo test, Node면 npm test). PR 올리기 전 반드시 로컬에서 통과 확인.

2. 기여 항목 찾기 — 일반 원칙

  • good first issue, help wanted 라벨을 먼저 확인 — 초심자를 배려해 스코프를 좁혀둔 이슈들.
  • 프로젝트에 “미구현 기능 추적” 성격의 트래킹 이슈가 있는지 확인 (예: GlueSQL의 #1684). 있으면 착수 후보를 고르기 가장 쉬운 곳.
  • 소스코드에서 직접 미구현/제한 지점을 찾는 방법 — 언어/프로젝트마다 표현 방식이 다르다:
    • TODO, FIXME 주석 — 가장 흔하지만 모든 프로젝트가 이렇게 표시하진 않음
    • todo!(), unimplemented!(), NotImplementedError 같은 언어별 “미구현” 매크로/예외
    • 에러 메시지에 “not supported”류 문자열 — GlueSQL은 이 방식(*NotSupported 에러 variant 110여 개)을 씀. 실제 조사 과정은 [[[프로젝트] GlueSQL 기여항목 찾기|GlueSQL 기여항목 찾기]] 참고
    • 인터페이스/트레이트는 정의돼 있는데 구현체 일부만 실제로 구현된 경우(스텁이 기본값만 반환)
  • 착수 전 반드시 담당자 유무를 확인한다 — 이슈 코멘트에 “I’ll take this” 같은 선점 표시가 있는지, 없으면 스스로 “작업 시작합니다” 코멘트를 남겨 중복 작업을 방지한다.
  • 이슈 없이 바로 PR을 올려도 되는지는 프로젝트마다 다르다. 스코프가 작고 자기 완결적인 버그 수정은 대부분 허용되지만, 새 설계 판단이 들어가는 변경은 이슈로 먼저 합의를 받는 쪽이 리스크가 적다.

3. Fork 기반 워크플로 — 회사 GitLab 방식과의 차이

회사([[[Git] 브랜치 관리 전략 (Branch Management Gu - 핵심 개념 및 특징 정리|브랜치 관리 전략]])는 하나의 원격 저장소(origin)에 직접 브랜치를 만들지만, 오픈소스는 보통 두 개의 원격 저장소를 다룬다.

개념회사(GitLab) 방식오픈소스(GitHub fork) 방식
저장소하나의 공유 저장소에 직접 브랜치원본을 내 계정으로 fork → 내 저장소에서 작업
원격(remote)origin 하나origin(내 fork) + upstream(원본) 두 개
브랜치 생성 위치공유 저장소 안에서 바로 생성내 fork에서 생성, 원본에는 브랜치를 만들지 않음
병합 요청Merge Request(MR), 팀 내 리뷰어 지정Pull Request(PR), 메인테이너/외부 기여자가 리뷰
최신화git pullgit fetch upstreammain 브랜치에 병합 후 fork에도 push

기본 순서:

# 1. GitHub 웹에서 Fork 버튼으로 내 계정에 복제
git clone https://github.com/<내계>/<repo>.git
cd <repo>
git remote add upstream https://github.com/<원본계>/<repo>.git
 
# 2. 작업 전 항상 upstream 기준으로 최신화
git fetch upstream main
git switch main
git merge --ff-only upstream/main   # 로컬 main이 upstream과 다르면 실패 → 이력이 꼬였다는 신호
git push origin main                # 내 fork의 main도 최신으로 맞춤
 
# 3. 작업 브랜치 생성 (네이밍은 프로젝트 관례를 따름 — 소문자/하이픈 조합이 일반적)
git switch -c fix/short-description

4. 로컬 개발 사이클

  1. 현재 동작을 먼저 재현한다. 고치기 전에 관련 테스트를 돌려보거나 최소 재현 코드/쿼리로 지금 상태를 확인 — “무엇이 바뀌었는지”를 스스로 증명할 수 있어야 한다.
  2. 기존 패턴을 검색해서 따라간다. 비슷한 기능이 이미 있으면 그 코드의 구조·네이밍· 테스트 스타일을 그대로 템플릿 삼는다. 새 스타일을 만들지 않는다.
  3. 작은 단위로 커밋한다. 하나의 커밋이 하나의 논리적 변경을 담도록 — 리뷰어가 diff를 따라가기 쉬워진다.
  4. 테스트를 추가/확장한다. 새 동작이나 새 에러 경로에는 반드시 테스트를 붙인다.
  5. PR 제출 전 로컬 검증: 프로젝트가 요구하는 linter/formatter/전체 테스트를 전부 통과시킨 뒤에 올린다(CI가 실패하면 리뷰가 밀린다).

5. 커밋/PR 제목 컨벤션 — “프로젝트마다 다르다”는 것 자체가 규칙

회사([[[Git] COMMIT 컨벤션 가이드 - 핵심 개념 및 특징 정리|COMMIT 컨벤션 가이드]])처럼 feat/fix/refactor 같은 타입 접두사를 강제하는 프로젝트(Conventional Commits 계열)도 있지만, 모든 오픈소스 프로젝트가 이 방식을 쓰는 건 아니다.

  • 예: GlueSQL은 타입 접두사를 쓰지 않는다. 최근 머지 PR 30개를 직접 조사한 결과 전부(예외 1건 제외) 동사원형으로 시작하는 평서문 제목이었다 — 자세한 근거는 [[[프로젝트] GlueSQL PR-이슈 작성법|GlueSQL PR-이슈 작성법]] 0절 참고.
  • 결론: “이 프로젝트가 Conventional Commits를 쓰는지”는 추측하지 말고, 최근 머지된 커밋/PR 히스토리를 직접 열어서 확인한다. CONTRIBUTING.md에 명시돼 있으면 그것을 따르고, 없으면 최근 이력의 다수 패턴을 따른다.

6. 이슈 작성 — 일반 원칙

프로젝트마다 템플릿은 다르지만 공통적으로 요구되는 정보는 같다.

  • 무엇이 문제인지 (버그면 재현 가능한 최소 예시, 기능 제안이면 왜 필요한지)
  • 기대 동작과 실제 동작의 차이
  • 재현 가능한 형태로 제공 — 코드/커맨드를 그대로 복붙해서 재현할 수 있게. “가끔 안 돼요” 같은 서술은 메인테이너가 판단하기 어렵다.
  • 기능 제안이라면 막연한 아이디어보다 구체적 시그니처/예시 코드를 먼저 스케치해서 보여주면 메인테이너가 “이 방향이 맞다/아니다”를 빠르게 판단할 수 있다.

7. PR 작성 — 일반 원칙

GitHub 생태계에서 사실상 표준으로 자리잡은 형태:

**Summary**
- 왜 이 변경이 필요한가 (문제/동기)
- 무엇을 바꿨는가 (핵심 변경사항)
- (필요시) 왜 이 방식을 택했는가 — 대안과 트레이드오프

**Test plan**
- [x] 실행한 테스트 명령어 + 결과 (통과 개수 등, 구체적으로)
- [x] linter/formatter 통과 여부

Fixes #NNN   ← 머지되면 해당 이슈가 자동으로 닫힘 (Closes/Resolves도 동일)
  • PR은 작을수록 리뷰가 빠르다. 여러 관심사가 섞인 PR은 쪼개는 것이 좋다.
  • 작업 중임을 알리고 피드백을 미리 받고 싶으면 Draft PR로 올린다.
  • Fixes/Closes/Resolves + #이슈번호 키워드는 대소문자 구분 없이 동작하며, 복수 이슈도 나열 가능(Fixes #1, Fixes #2). 자동 종료 없이 그냥 참조만 걸고 싶으면 키워드 없이 #N만 언급한다.

8. 리뷰 대응

  • 리뷰 코멘트에 대한 수정은 기존 커밋에 fixup/추가 커밋으로 반영하고, 리뷰가 끝나기 전까지는 force-push(히스토리 재작성)를 피한다 — 리뷰어가 diff 변화를 추적하기 어려워진다. 머지 직전에 정리(squash/rebase)가 필요하면 메인테이너 안내를 따른다.
  • 논의가 여러 갈래로 길어지면, 스코프를 벗어나는 부분은 별도 이슈로 분리하고 현재 PR은 원래 스코프에 집중한다.

9. 머지 이후

  • 작업 브랜치는 삭제하고, upstream/main을 다시 fetch해서 로컬/fork main을 최신화한다.
  • 같은 영역에서 후속 개선점이 보이면 새 이슈/PR로 이어간다 — 실제로 GlueSQL에서 PR #1972(버그 수정) 이후 관련 구조 개선을 PR #1980으로 이어서 진행한 사례가 있다.